]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
status/volume: Redundant volume pushes cause DSP crackling
authorAndre Eikmeyer <dev@deq.rocks>
Mon, 27 Jul 2026 11:57:25 +0000 (13:57 +0200)
committerFlorian Müllner <fmuellner@gnome.org>
Sun, 2 Aug 2026 16:51:10 +0000 (18:51 +0200)
Dragging the Quick Settings volume slider generates a dense sequence of
value notifications. After GVC rounds a requested value and scales the
channel map, several consecutive notifications can result in the same
effective channel volumes.

The shell currently assigns the value through the GObject property and
calls push_volume() unconditionally. The property setter discards the
boolean returned by gvc_mixer_stream_set_volume(), so unchanged values are
sent to the audio backend again.

This is audible with PipeWire DSP sinks. Volume changes pass through a
virtual filter sink and its processing graph before reaching the hardware
sink. The redundant pushes repeatedly update that control path while the
slider is moving, producing crackles. Native hardware sinks make the same
shell behavior much less apparent.

GNOME Settings does not show the problem because its volume slider calls
gvc_mixer_stream_set_volume() directly and only pushes the volume when the
effective channel map changed.

The Quick Settings slider now follows the same behavior and reuses the
returned change state for notification handling.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4302>
(cherry picked from commit bd5f0a10d101d2ac2110ea8e902eb9515749324c)

js/ui/status/volume.js

index fc49cb9ef7a1b210114055e8024f1491a27d5c7a..a01cea36a070220e9c04e7d241fcc07561162ad8 100644 (file)
@@ -180,19 +180,19 @@ const StreamSlider = GObject.registerClass({
         const value = this.slider.value;
         const volume = value * this._control.get_vol_max_norm();
         const prevMuted = this._stream.is_muted;
-        const prevVolume = this._stream.volume;
+        let volumeChanged;
         if (volume < 1) {
-            this._stream.volume = 0;
+            volumeChanged = this._stream.set_volume(0);
             if (!prevMuted)
                 this._stream.change_is_muted(true);
         } else {
-            this._stream.volume = volume;
+            volumeChanged = this._stream.set_volume(volume);
             if (prevMuted)
                 this._stream.change_is_muted(false);
         }
-        this._stream.push_volume();
+        if (volumeChanged)
+            this._stream.push_volume();
 
-        const volumeChanged = this._stream.volume !== prevVolume;
         if (volumeChanged && !this._notifyVolumeChangeId && !this._inDrag) {
             this._notifyVolumeChangeId = GLib.timeout_add_once(GLib.PRIORITY_DEFAULT, 30, () => {
                 this._notifyVolumeChange();